/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package replica.transaction;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Logger;
import replica.ReplicaLoggerFactory;
import java.util.List;
import java.util.LinkedList;

/**
 *
 * @author andrea
 */
public class PendingOperationQueue
{
    public static final int size=1000000;
    
    ArrayBlockingQueue<Operation> operations=new ArrayBlockingQueue<Operation>(size);
    
    private boolean peekingState=false;
    private Logger logger;

    private long nodeId;
    private long lastSerial=0; //PER DEBUG
    
    public PendingOperationQueue(long nodeId)
    {
        this.nodeId=nodeId;
        logger=ReplicaLoggerFactory.getPendingOperationLogger(nodeId, 0);
    }
    
    public void add(Operation operation) throws IllegalStateException
    {
        try{
            //PER DEBUG
            
                if (operation.getOperationType()!=Operation.OperationType.GETLIST)
                {
                    if (operation.getSerialNumber()!= lastSerial+1)
                        throw new Exception("lastSerial="+lastSerial+"; operation="+operation);

                    lastSerial=operation.getSerialNumber();
                }
                
                if(this.nodeId!=operation.getNodeId())
                    throw new Exception("this.nodeId="+this.nodeId+"; operation.getNodeId()="+operation.getNodeId());
            
            //FINE PER DEBUG
            
            
            operations.add(operation);
            logger.info("Operation "+
                    operation+"added. Now pendingQueue=</br>"+operations+"</br></br>");
            
            synchronized(operations){
                operations.notify();
            }
            
            
        }
        catch(IllegalStateException ise)
        {
            throw new IllegalStateException("pendingOperationsQueue is full");
        }catch(Exception e) {e.printStackTrace();}
    }
    
    /**
     * Returns the head of the queue if the queue is not empty. If the queue is
     * empty, it blocks until an element becomes available
     * @return 
     */
    public Operation blockingPeek()
    {
        Operation operation=null;
        try{
            if (peekingState==true)
                throw new IllegalStateException("Error: two cuncurrent blockingPeeks "
                        + "are not expected");

            this.peekingState=true;
            operation=operations.peek();
            if (operation==null)
            {
                synchronized(operations){
                    operations.wait();
                }
                //If someone notified on operations, an operation is provided
                operation=operations.peek();
                
                //PER DEBUG
                if (operation==null)
                    throw new IllegalStateException("I was notified but no operation"
                            + " was provided. This is forbidden");
                //FINE PER DEBUG
            }
        }catch(IllegalStateException ise)
        {ise.printStackTrace();System.exit(-1246);}
        catch(InterruptedException ie)
        {ie.printStackTrace();System.exit(-1246);}
            
        return operation;
    }
    
    public Operation take()
    {
        Operation operation=null;
        try{
            //PER DEBUG
            Operation operationProva=operations.peek();
            
            if(this.peekingState==false)
                throw new IllegalStateException("PendingOperationQueue of node "+nodeId+
                        ". Trying to directly remove. This is "
                        + "forbidden. you must call blockingPeek at first");

            if (operationProva==null)
                throw new NullPointerException("PendingOperationQueue of node "+nodeId+
                        ". No operation found while executing "
                        + "take(). This is forbidden");
            //FINE PER DEBUG
    
            operation=operations.take();
            logger.info("Operation  "
                    +operation+" was taken. Now pendingQueue=</br>"+operations+"</br></br>");
        }catch(Exception e)
        {e.printStackTrace();System.exit(-1456);}
        
        this.peekingState=false;
        return operation;
    }
    
    public void dontTake()
    {
        try{
            //PER DEBUG
            Operation operation=operations.peek();
            if(this.peekingState==false)
                throw new IllegalStateException("PendingOperationQueue of node "+nodeId+
                        "Trying to directly access operations. This is "
                        + "forbidden. you must call blockingPeek at first");

            if (operation==null)
                throw new NullPointerException("PendingOperationQueue of node "+nodeId+
                        "No operation found while executing "
                        + "take(). This is forbidden");
            
            }catch(Exception e)
        {e.printStackTrace();System.exit(-1456);}
        //FINE PER DEBUG
        
        
        this.peekingState=false;
        
        System.out.println("!!!PendingOperationQueue: e' stato fatto il don't take. "
                + ". Adesso pendingQueue=" + operations);
        
    }

    public void setPeekingState(boolean peekingState) {
        this.peekingState = peekingState;
    }

    void setOnRebirth() 
    {
        this.peekingState=false;
    }

    
    
    
    
}
